+++ /dev/null
-// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported init, addCaller, addTween, getTweenCount, removeTweens,
- pauseTweens, resumeTweens, registerSpecialProperty,
- registerSpecialPropertyModifier, registerSpecialPropertySplitter */
-
-const { Clutter, GLib, Shell } = imports.gi;
-const Signals = imports.signals;
-const Tweener = imports.tweener.tweener;
-
-const { adjustAnimationTime } = imports.ui.environment;
-
-// This is a wrapper around imports.tweener.tweener that adds a bit of
-// Clutter integration. If the tweening target is a Clutter.Actor, then
-// the tweenings will automatically be removed if the actor is destroyed.
-
-// ActionScript Tweener methods that imports.tweener.tweener doesn't
-// currently implement: getTweens, getVersion, registerTransition,
-// setTimeScale, updateTime.
-
-// imports.tweener.tweener methods that we don't re-export:
-// pauseAllTweens, removeAllTweens, resumeAllTweens. (It would be hard
-// to clean up properly after removeAllTweens, and also, any code that
-// calls any of these is almost certainly wrong anyway, because they
-// affect the entire application.)
-
-// Called from Main.start
-function init() {
- Tweener.setFrameTicker(new ClutterFrameTicker());
-}
-
-
-function addCaller(target, tweeningParameters) {
- _wrapTweening(target, tweeningParameters);
- Tweener.addCaller(target, tweeningParameters);
-}
-
-function addTween(target, tweeningParameters) {
- _wrapTweening(target, tweeningParameters);
- Tweener.addTween(target, tweeningParameters);
-}
-
-function _wrapTweening(target, tweeningParameters) {
- let state = _getTweenState(target);
-
- if (!state.destroyedId) {
- if (target instanceof Clutter.Actor) {
- state.actor = target;
- state.destroyedId = target.connect('destroy', _actorDestroyed);
- } else if (target.actor && target.actor instanceof Clutter.Actor) {
- state.actor = target.actor;
- state.destroyedId = target.actor.connect('destroy', () => _actorDestroyed(target));
- }
- }
-
- let { time, delay } = tweeningParameters;
- if (!isNaN(time))
- tweeningParameters['time'] = adjustAnimationTime(1000 * time) / 1000;
- if (!isNaN(delay))
- tweeningParameters['delay'] = adjustAnimationTime(1000 * delay) / 1000;
-
- _addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
-}
-
-function _getTweenState(target) {
- // If we were paranoid, we could keep a plist mapping targets to
- // states... but we're not that paranoid.
- if (!target.__ShellTweenerState)
- target.__ShellTweenerState = {};
- return target.__ShellTweenerState;
-}
-
-function _resetTweenState(target) {
- let state = target.__ShellTweenerState;
-
- if (state) {
- if (state.destroyedId)
- state.actor.disconnect(state.destroyedId);
- }
-
- target.__ShellTweenerState = {};
-}
-
-function _addHandler(target, params, name, handler) {
- if (params[name]) {
- let oldHandler = params[name];
- let oldScope = params[`${name}Scope`];
- let oldParams = params[`${name}Params`];
- let eventScope = oldScope ? oldScope : target;
-
- params[name] = () => {
- oldHandler.apply(eventScope, oldParams);
- handler(target);
- };
- } else {
- params[name] = () => handler(target);
- }
-}
-
-function _actorDestroyed(target) {
- _resetTweenState(target);
- Tweener.removeTweens(target);
-}
-
-function _tweenCompleted(target) {
- if (!isTweening(target))
- _resetTweenState(target);
-}
-
-function getTweenCount(scope) {
- return Tweener.getTweenCount(scope);
-}
-
-// imports.tweener.tweener doesn't provide this method (which exists
-// in the ActionScript version) but it's easy to implement.
-function isTweening(scope) {
- return Tweener.getTweenCount(scope) != 0;
-}
-
-function removeTweens(...args) {
- if (Tweener.removeTweens(args)) {
- let [scope] = args;
- // If we just removed the last active tween, clean up
- if (Tweener.getTweenCount(scope) == 0)
- _tweenCompleted(scope);
- return true;
- } else {
- return false;
- }
-}
-
-function pauseTweens(...args) {
- return Tweener.pauseTweens(...args);
-}
-
-function resumeTweens(...args) {
- return Tweener.resumeTweens(...args);
-}
-
-
-function registerSpecialProperty(...args) {
- Tweener.registerSpecialProperty(...args);
-}
-
-function registerSpecialPropertyModifier(name, modifyFunction, getFunction) {
- Tweener.registerSpecialPropertyModifier(name, modifyFunction, getFunction);
-}
-
-function registerSpecialPropertySplitter(name, splitFunction, parameters) {
- Tweener.registerSpecialPropertySplitter(name, splitFunction, parameters);
-}
-
-
-// The 'FrameTicker' object is an object used to feed new frames to
-// Tweener so it can update values and redraw. The default frame
-// ticker for Tweener just uses a simple timeout at a fixed frame rate
-// and has no idea of "catching up" by dropping frames.
-//
-// We substitute it with custom frame ticker here that connects
-// Tweener to a Clutter.TimeLine. Now, Clutter.Timeline itself isn't a
-// whole lot more sophisticated than a simple timeout at a fixed frame
-// rate, but at least it knows how to drop frames. (See
-// HippoAnimationManager for a more sophisticated view of continuous
-// time updates; even better is to pay attention to the vertical
-// vblank and sync to that when possible.)
-//
-var ClutterFrameTicker = class {
- constructor() {
- // We don't have a finite duration; tweener will tell us to stop
- // when we need to stop, so use 1000 seconds as "infinity", and
- // set the timeline to loop. Doing this means we have to track
- // time ourselves, since clutter timeline's time will cycle
- // instead of strictly increase.
- this._timeline = new Clutter.Timeline({ duration: 1000 * 1000 });
- this._timeline.set_loop(true);
- this._startTime = -1;
- this._currentTime = -1;
-
- this._timeline.connect('new-frame', () => {
- this._onNewFrame();
- });
-
- let perfLog = Shell.PerfLog.get_default();
- perfLog.define_event("tweener.framePrepareStart",
- "Start of a new animation frame",
- "");
- perfLog.define_event("tweener.framePrepareDone",
- "Finished preparing frame",
- "");
- }
-
- get FRAME_RATE() {
- return 60;
- }
-
- _onNewFrame() {
- // If there is a lot of setup to start the animation, then
- // first frame number we get from clutter might be a long ways
- // into the animation (or the animation might even be done).
- // That looks bad, so we always start at the first frame of the
- // animation then only do frame dropping from there.
- if (this._startTime < 0)
- this._startTime = GLib.get_monotonic_time() / 1000.0;
-
- // currentTime is in milliseconds
- let perfLog = Shell.PerfLog.get_default();
- this._currentTime = GLib.get_monotonic_time() / 1000.0 - this._startTime;
- perfLog.event("tweener.framePrepareStart");
- this.emit('prepare-frame');
- perfLog.event("tweener.framePrepareDone");
- }
-
- getTime() {
- return this._currentTime;
- }
-
- start() {
- this._timeline.start();
- global.begin_work();
- }
-
- stop() {
- this._timeline.stop();
- this._startTime = -1;
- this._currentTime = -1;
- global.end_work();
- }
-};
-Signals.addSignalMethods(ClutterFrameTicker.prototype);